﻿#include <opencv2/opencv.hpp>;
using namespace cv;
using namespace std;

/*
void calcHist(
	const Mat* images,                  //输入的图像的指针，可以是多幅图像，所有的图像必须有同样的深度（CV_8U or CV_32F）。同时一副图像可以有多个channes。需用地址符&。
	int nimages,                        //输入图像的个数
	const int* channels,                //需要统计直方图的第几通道。用来计算直方图的channes的数组。比如输入是2副图像，第一副图像有0，1，2共三个channel，第二幅图像只有0一个channel，那么输入就一共有4个channes，如果int channels[3] = {3, 2, 0}，那么就表示是使用第二副图像的第一个通道和第一副图像的第2和第0个通道来计算直方图。
	InputArray mask,                    //可选的操作掩码， 用Mat()
	OutputArray hist,                   //输出的目标直方图
	int dims,                           //需要统计直方图通道的个数
	const int* histSize,                //指的是直方图横轴分成多少个区间，就是bin的个数。在每一维上直方图的个数。简单把直方图看作一个一个的竖条的话，就是每一维上竖条的个数。即可由bin个数求得对应要统计的灰度值。
	const float** ranges,               //统计的数据（像素的灰度值）的区间（取值范围），必须要用const。比如： 
                                                        float rang1[] = {0, 20};
                                                        float rang2[] = {30, 40};
                                                        const float *rangs[] = {rang1, rang2};那么就是对0，20和30，40范围的灰度值进行统计。
	bool uniform = true,                //默认true，是否对得到的直方图数组进行归一化处理
	bool accumulate = false             //默认false，在多个图像时，是否累计计算像素值得个数
	);            
*/

int main(void)
{
	//1、载入图像
	const char *fig = ("G:\\Engineering software\\OpenCV4.0.1\\opencv-4.0.1\\samples\\data\\pic2.png");
	Mat image = imread(fig, 0);
	if (image.empty())
	{
		printf("Picture loading failed !\n");
		system("Pause");
		return -1;
	}

	//2、输出图像的灰度图
	imshow("灰度图", image);
	waitKey();

	//3、计算灰度图像的一维直方图
	const int nimages = 1;                 //可不定义变量，直接在函数内用数值1表示。
	int channels[] = { 0 };                //可不定义变量，直接在函数内用数值表示。定义求直方图的通道数目，从0开始索引。
	Mat hist;
	int dims = 1;                          //可不定义变量，直接在函数内用数值1表示。
	int histSize[] = { 256 };              //必须定义一个数组，并定义为int型。定义直方图的在每一维上的大小，例如灰度图直方图的横坐标是图像的灰度值，就一维，bin的个数，y轴是每个bin上统计的数目。
	float range[] = { 0.0,255.0 };         //必须定义一个数组，并定义为float型，且定义指针。每一维bin的变化范围。
	const float* ranges[] = { range };
	calcHist(&image, nimages, channels, Mat(), hist, dims, histSize, ranges, true, false);

	//4、获取灰度直方图最大最小值
	double maxValue = 0;
	double minValue = 0;
	minMaxLoc(hist, &minValue, &maxValue); //寻找矩阵(一维数组当作向量,用Mat定义) 中最小值和最大值的位置。  

	//5、直方图图像显示
	Mat histImage(histSize[0], histSize[0], CV_8U, Scalar(0));//用统计的灰度值（bin）个数为长度定义一个256*256的黑色底板
	int hpt = saturate_cast<int>(0.9 * histSize[0]);
	for (int i = 0; i < 256; i++)
	{
		float binValue = hist.at<float>(i);
		//统计数值的缩放，增强直方图的可视化
		int realValue = saturate_cast<int>(binValue * hpt / maxValue);
		//在256*256的黑色底板上画矩形
		rectangle(histImage, Point(i, histSize[0] - 1), Point(i + 1, histSize[0] - realValue), Scalar(255));
	}
	imshow("灰度直方图", histImage);
	waitKey();

	destroyAllWindows();

	//显示彩色的源图片
	Mat image1 = imread("G:\\Engineering software\\OpenCV4.0.1\\opencv-4.0.1\\samples\\data\\pic2.png");
	Mat image2 = imread("G:\\Engineering software\\OpenCV4.0.1\\opencv-4.0.1\\samples\\data\\pic6.png");
	imshow("源图片1", image1);
	imshow("源图片2", image2);
	waitKey();

	//大津算法Otsu分割图片

	/*
	threshold(src, thresh, maxval, type[, dst])
		src：输入图像。
		dst：目标图像。
		thresh：当前阈值。
		maxVal：最大阈值，一般为255.
		thresholdType:阈值类型，主要有下面几种：
		THRESH_BINARY = 0,
		THRESH_BINARY_INV = 1,
		THRESH_TRUNC = 2,
		THRESH_TOZERO = 3,
		THRESH_TOZERO_INV = 4,
		THRESH_MASK = 7,
		THRESH_OTSU = 8,
		THRESH_TRIANGLE = 16
	*/

	Mat gray[2];
	cvtColor(image1, gray[0], COLOR_BGR2GRAY);
	cvtColor(image2, gray[1], COLOR_BGR2GRAY);
	//medianBlur(gray[0], gray[0], 5);
	Mat threshold_image[2];
	threshold(gray[0], threshold_image[0], 0, 255, THRESH_OTSU); //type参数THRESH_OTSU对应的是自动阈值分割的OTSU算法，也可用8代替。
	threshold(gray[1], threshold_image[1], 0, 255, 8); //maxval参数255对应的是最大阈值，OTSU算法中应该是指可以遍历的最大阈值； thresh参数0对应的是当前阈值，对OTSU算法应该没有影响？？？
	imshow("大津算法阈值分割的图片1", threshold_image[0]);
	imshow("大津算法阈值分割的图片2", threshold_image[1]);
	waitKey();

	//区域生长法floodFill分割图片

	/*
	区域生长/漫水填充/泛洪算法：
	int cv::floodFill(InputOutputArray image, Point seedPoint, Scalar newVal, Rect *rect=0, Scalar loDiff = Scale(), Scalar upDiff = Scale(), int flags = 4);
	·InputOutputArray类型的image, 输入/输出1通道或3通道，8位或浮点图像；
	·Point类型的seedPoint，漫水填充算法的起始点，根据该点的像素判断决定和其相近颜色的像素点，是否被泛洪处理；
	·Scalar类型的newVal，像素点被染色的值，即在重绘区域像素的新值（B,G,R）；
	·Rect*类型的rect，有默认值0，一个可选的参数，用于设置floodFill函数将要重绘区域的最小边界矩形区域；
	·Scalar类型的loDiff，有默认值Scalar( )，表示当前观察像素值与其部件邻域像素值或者待加入该部件的种子像素之间的亮度或颜色之负差（lower brightness/color difference）的最大值；即相对于seed种子点像素可以往下的像素值，即seedPoint(B0,G0,R0)，泛洪区域下界为（B0-loDiff1,G0-loDiff2,R0-loDiff3）；
	·Scalar类型的upDiff，有默认值Scalar( )，表示当前观察像素值与其部件邻域像素值或者待加入该部件的种子像素之间的亮度或颜色之正差（lower brightness/color difference）的最大值；即相对于seed种子点像素可以往上的像素值，即seedPoint(B0,G0,R0)，泛洪区域上界为（B0+upDiff1,G0+upDiff2,R0+upDiff3）；
	·int类型的flags，操作标志符，此参数包含三个部分：
			1、低八位（第0~7位）用于控制算法的连通性，可取4 (4为缺省值) 或者 8。如果设为4，表示填充算法只考虑当前像素水平方向和垂直方向的相邻点；如果设为 8，除上述相邻点外，还会包含对角线方向的相邻点。
			2、高八位部分（16~23位）可以为0 或者如下两种选项标识符的组合：<1>FLOODFILL_FIXED_RANGE - 如果设置为这个标识符的话，就会考虑当前像素与种子像素之间的差，否则就考虑当前像素与其相邻像素的差。也就是说，这个范围是浮动的。<2>FLOODFILL_MASK_ONLY - 如果设置为这个标识符的话，函数不会去填充改变原始图像 (也就是忽略第三个参数newVal), 而是去填充掩模图像（mask）。这个标识符只对第二个版本的floodFill有用，因第一个版本里面压根就没有mask参数。
			3、中间八位部分，上面关于高八位FLOODFILL_MASK_ONLY标识符中已经说的很明显，需要输入符合要求的掩码。Floodfill的flags参数的中间八位的值就是用于指定填充掩码图像的值的。但如果flags中间八位的值为0，则掩码会用1来填充。
	*/

	Rect rect;
	floodFill(gray[0], Point(10, 10), Scalar(255, 255, 255), 0, Scalar(0, 0, 0), Scalar(1, 1, 1));
	floodFill(gray[1], Point(10, 10), Scalar(255, 255, 255), &rect, Scalar(0, 0, 0), Scalar(0, 0, 0));
	imshow("区域生长分割的图片1", gray[0]);
	imshow("区域生长分割的图片2", gray[1]);
	waitKey();
	destroyAllWindows();

}



